﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace yc.Site
{
	/// <summary>
	/// 权限验证
	/// </summary>
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
	public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
	{
		static string[] UnAuthorizeControllers = new[] { "LOGIN" };
		static string[] AuthorizeAdminControllers = new[] { "DICTIONARY" };

		// This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method.
		/// <summary>
		/// 验证页面权限
		/// </summary>
		/// <param name="httpContext"></param>
		/// <returns></returns>
		protected virtual bool AuthorizeCore(HttpContextBase httpContext)
		{
			//页面没有初始化
			if (httpContext == null) throw new ArgumentNullException("httpContext");

			return Common.IsAuthenticated;
		}

		protected virtual bool AuthorizeAdmin(HttpContextBase httpContext)
		{
			//页面没有初始化
			if (httpContext == null) throw new ArgumentNullException("httpContext");

			if (AuthorizeAdminControllers.Contains(Common.Controller)) return Common.User.Available;
			return true;
		}


		private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
		{
			validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
		}

		/// <summary>
		/// 页面验证方法
		/// </summary>
		/// <param name="filterContext"></param>
		public virtual void OnAuthorization(AuthorizationContext filterContext)
		{
			if (filterContext == null) throw new ArgumentNullException("filterContext");

			//判断页面是否需要登录
			if (UnAuthorizeControllers.Contains(Common.Controller)) return;

			if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
			{
				// If a child action cache block is active, we need to fail immediately, even if authorization
				// would have succeeded. The reason is that there's no way to hook a callback to rerun
				// authorization before the fragment is served from the cache, so we can't guarantee that this
				// filter will be re-run on subsequent requests.
				throw new InvalidOperationException("AuthorizeAttribute cannot be used within a child action caching block.");
			}

			//bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
			//						 || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

			//if (skipAuthorization) return;

			if (AuthorizeCore(filterContext.HttpContext) && AuthorizeAdmin(filterContext.HttpContext))
			{
				// ** IMPORTANT **
				// Since we're performing authorization at the action level, the authorization code runs
				// after the output caching module. In the worst case this could allow an authorized user
				// to cause the page to be cached, then an unauthorized user would later be served the
				// cached page. We work around this by telling proxies not to cache the sensitive page,
				// then we hook our custom authorization code into the caching mechanism so that we have
				// the final say on whether a page should be served from the cache.

				HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
				cachePolicy.SetProxyMaxAge(new TimeSpan(0));
				cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
			}
			else
			{
				HandleUnauthorizedRequest(filterContext);//用户没有登录
			}
		}

		/// <summary>
		/// 未登录页面
		/// </summary>
		/// <param name="filterContext"></param>
		protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
		{
			// Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
			filterContext.Result = new HttpUnauthorizedResult();
		}

		/// <summary>
		/// 验证页面状态
		/// This method must be thread-safe since it is called by the caching module.
		/// </summary>
		/// <param name="httpContext"></param>
		/// <returns></returns>
		protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
		{
			if (httpContext == null) throw new ArgumentNullException("httpContext");

			bool isAuthorized = AuthorizeCore(httpContext);
			return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
		}
	}
}